/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief Implementation of class CParameterData
 * \author Matthieu Myrsky <matthieu.myrsky@tkk.fi>
 *
 * $Id: ParameterData.cpp,v 1.2 2009-09-21 12:57:59 morsko Exp $
 *
 */
#include "ParameterData.hpp"
#include "owndebug.h"
#include "ParameterXML.hpp"

//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
using namespace MaCI::Parameter;
//*****************************************************************************

CParameterData::CParameterData()
  :iTimestampPtr(NULL),
   iCommandPtr(NULL),
   iParameterXMLPtr(NULL)
{
  SetLastElementType(KTypeUnknown);
}
//*****************************************************************************

CParameterData::~CParameterData()
{
}
//*****************************************************************************

CParameterData::CParameterData(const CParameterData &aData)
  :iTimestampPtr(NULL),
   iCommandPtr(NULL),
   iParameterXMLPtr(NULL)
{
 // Call assignment operator.
  *this = aData;
}
//*****************************************************************************

CParameterData &CParameterData::operator=(const CParameterData &aData)
{
  // Reset
  Reset();

  // Create an internal BinBag instance
  if (CreateInternalBinBag(aData.iBinBagContainer) == false) {
    dPrint(4,"WARNING: Failed to create internal binbag for object %p from object %p",
           this, &aData);
  }
  
  // Do the decode on the buffer; it sets pointers and ... stuff :)
  if (DecodeFrom(NULL) == false) {
    dPrint(4,"Warning: Failed to Decode CSQLData even though assignment operator used?");
  }
  
  return *this; 
}
//*****************************************************************************

bool CParameterData::DecodeFrom(gim::binbag::CBinBag *aBinBag)
{
   bool result = true;
  const gim::binbag::TBinBlob *b = NULL;
  int previous_type = -1;
  
  // Store pointer and test it for NullNess. only overwrite if the
  // argument was non-NULL.
  if (aBinBag != NULL) 
    SetBinBagContainerPtr(aBinBag);

  // Parse
  if (iBinBagContainer && iBinBagContainer->Rewind()) {
    while( (b = iBinBagContainer->GetNextBinary()) && result ) {
      switch(b->type) {

      case KTypeTimestamp:
	if (iTimestampPtr == NULL && 
            b->size == sizeof(Common::TTimestamp)) {
	  iTimestampPtr = reinterpret_cast<const Common::TTimestamp *>(b->data);

	} else result = false;
	break;

      case KTypeCommand: {
        if (iCommandPtr == NULL && 
            b->size == sizeof(TCommand)) {
          iCommandPtr = reinterpret_cast<const TCommand *>(b->data); 

        } else result = false;
        break;
      }
      case KTypeParameterXML:{
        if(iParameterXMLPtr == NULL &&
           b->size >= sizeof(TParameterXML)) {
	  iParameterXMLPtr = reinterpret_cast<const TParameterXML *>(b->data);
          // Check size validity (required on variable size fields)
          if(iParameterXMLPtr->length + sizeof(TParameterXML) != b->size){
            
            iParameterXMLPtr = NULL;
            result = false;
          }
	} else result = false;
	break;

      }
     

      default:
	// Unknown Type in BinBag. Do nothing.
	dPrint(10,"Unknown type %u, ignoring.", b->type);
        break;
      }
      
      // Store previous type
      previous_type = b->type;
    }
  } else result = false;

  // Store the result
  SetReadOnlyContainer(result);

  return result;
}
//*****************************************************************************

void CParameterData::Reset()
{
 // Call base class reset.
  CMaCIData::Reset();

  // Clean out. (Same values as in default constructor)
  iTimestampPtr = NULL;
  iCommandPtr = NULL;
  iParameterXMLPtr = NULL;

  SetLastElementType(KTypeUnknown);
}
//*****************************************************************************

void CParameterData::Print(const int level) const
{
  const gim::binbag::TBinBlob *b = NULL;
  // Parse
  if (iBinBagContainer && iBinBagContainer->Rewind()) {
    while( (b = iBinBagContainer->GetNextBinary())) {
      switch(b->type) {
      case KTypeTimestamp: {
	const Common::TTimestamp *ts = 
          reinterpret_cast<const Common::TTimestamp *>(b->data);
	dPrint(level,"TTimestamp: s=%d, us=%d", 
	       ts->timestamp_s,
	       ts->timestamp_us);
	break;
      }
      case KTypeCommand: {
	const TCommand *ts = 
          reinterpret_cast<const TCommand *>(b->data);
	dPrint(level,"TCommand: cmd=%d", 
	       ts->cmd);
	break;
      }

      case KTypeParameterXML:{
        const TParameterXML *p =
          reinterpret_cast<const TParameterXML *>(b->data);
        dPrint(level,
               "TParameterXML:\n"
               "***********************BEGIN******************************\n"
               "%s\n"
               "************************END*******************************\n",
               p->text);
        break;


      }

      default:
	// Unknown Type in BinBag. Do nothing.
	dPrint(10,"Unknown type %u, ignoring.", b->type);
        break;
      }
    }
  }
  
  
}
//*****************************************************************************

bool CParameterData::SetTimestamp(const Common::TTimestamp &aStamp)
{
   bool result = false;
  if (!IsReadOnlyContainer() && iTimestampPtr == NULL) {
    ADDELEMENT(Common::TTimestamp, KTypeTimestamp, iTimestampPtr, aStamp);
    result = true;
  }
  return result;
}
//*****************************************************************************

bool CParameterData::SetCommand(const TCommand &aCmd)
{
    bool result = false;
  if (!IsReadOnlyContainer() && iCommandPtr == NULL) {
    ADDELEMENT(TCommand, KTypeCommand, iCommandPtr, aCmd);
    result = true;
  }
  return result;
}
//*****************************************************************************

bool CParameterData::SetParameterXML(const TParameterXML &aXML)
{
  bool result = false;
  if (!IsReadOnlyContainer() && iParameterXMLPtr == NULL) {
    // Insertion.
    const gim::binbag::TBinBlob *b = 
      iBinBagContainer->AddBinary(KTypeParameterXML, 
                                  (const char *)&aXML, 
                                  aXML.GetContainerSize());
    iParameterXMLPtr = reinterpret_cast<const TParameterXML *>(b->data);
    SetLastElementType(KTypeParameterXML);
    result = true;
  }
  return result;



}
//*****************************************************************************

bool CParameterData::SetParameterXMLString(const std::string &aXMLString)
{
  bool result = false;
  uint8_t *ptr = NULL;
  unsigned int bytes = 0;

  // Create dynamic container from xmlString
  TParameterXML *tpxml = TParameterXML::CreateTextElement(&ptr, bytes, aXMLString);
  
  // Use the normal setter function. (use result)
  result = SetParameterXML(*tpxml);

  // Unconditionally destroy dynamic element.
  TParameterXML::DestroyTextElement(tpxml);

  return result;
}
//*****************************************************************************

const MaCI::Common::TTimestamp *CParameterData::GetTimestamp(void) const
{
  return iTimestampPtr;
}
//*****************************************************************************

const TCommand *CParameterData::GetCommand(void) const
{
   return iCommandPtr;
}
//*****************************************************************************

const TParameterXML *CParameterData::GetParameterXML(void) const
{
  return iParameterXMLPtr;
}
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
#ifdef COMPILE_PARAMETERDATA_STANDALONE_MAIN
#include <string>
#include <stdio.h>
int main(void)
{
 using namespace MaCI::Parameter;
 gim::binbag::CBinBag bb;
 CParameterData A;
 CParameterData B;
 bool r;

 debugInit();
 debugSetGlobalDebugLvl(5);
 printf("Carry out CParameterXML tests..\n");
 CParameterXML pxml;

 TParameterInfoArray array;
 TParameterInfo info(std::string("Brightness"),
                     std::string("Changes brightness of image"),
                     std::string("33"),
                     std::string("0-100"),
                     std::string(""));
 
 TParameterInfo info2(std::string("Contrast"),
                      std::string("Changes contrast of image"),
                      std::string("43"),
                      std::string("0-300"),
                      std::string(""));
 
 array.push_back(info);
 array.push_back(info2);
 pxml.SetParameterInfoArray(array);

 printf("Then encode it\n");
 r = pxml.EncodeParameterXML();
 assert (r ==true);
 std::string encodedString =  pxml.GetXMLData();
 
 printf("printing:\n%s",encodedString.c_str());
 printf("*******************************************************\n");
 printf("*******************************************************\n");
 printf("Testing decoding\n");
 //Decode string..



 CParameterXML pxml2;
 r=  pxml2.DecodeParameterXML(encodedString);
 assert(r==true);
 
 TParameterInfoArray array2;
 pxml2.GetParameters(array2);
 printf("printing array:\n");
 
 for(unsigned i = 0; i< array2.size(); i++){
   printf("%d.\n key: %s\n description: %s\n currentValue: %s\n validValues: %s\n validValuesRegExp: %s\n",
          i,
          array2[i].key.c_str(),
          array2[i].description.c_str(),
          array2[i].currentValue.c_str(),
          array2[i].validValues.c_str(),
          array2[i].validValuesRegExp.c_str());

 }
 printf("*******************************************************\n");
 printf("*******************************************************\n");
 printf("CParameterXML tests completed OK!\n");
 printf("*******************************************************\n");



 printf("Then test CParameterData-class\n");

 printf("Encoding...\n");
 // Do Encoding 
 A.EncodeTo(&bb);
  
 printf("Encode: Timestamp(1,2) ... ");
 r = A.SetTimestamp(MaCI::Common::TTimestamp(1, 2));
 assert(r == true);
 printf("OK.\n"); 

 printf("Encode: TCommand(KCommandGetParametersReply) ... ");
 r = A.SetCommand(TCommand( KCommandGetParametersReply));
 assert(r == true);
 printf("OK.\n");
 
 printf("Encode with SetParameterXMLString setting '%s'",encodedString.c_str());
 r = A.SetParameterXMLString(encodedString);
 assert(r == true);
 printf("OK.\n");

 printf("*******************************************************\n");
 printf("*******************************************************\n");

 printf("Decoding...\n");
 // Do Decoding
 B.DecodeFrom(&bb);

 printf("Decode: GetTimestamp() = (%d, %d) ... ", 
	 B.GetTimestamp()->timestamp_s, 
	 B.GetTimestamp()->timestamp_us);
 assert(B.GetTimestamp()->timestamp_s == 1);
 assert(B.GetTimestamp()->timestamp_us == 2);
 printf("Assertions OK.\n\n");

 ////////////////// GetCommand()
  printf("Decode: GetCommand() : \n");
  const TCommand *c = B.GetCommand();
  printf("Command: %d\n", c->cmd); 
  assert(c->cmd == KCommandGetParametersReply);
  printf("Assertions OK.\n\n");

  printf("Decode: GetParameterXML() : \n");
  const TParameterXML *p = B.GetParameterXML();
 
  printf("ParameterXML: %s\n", p->GetTextPtr()); 
    
  
  dPrint(1,"Testing Print()-fuction");
  B.Print(1);
  printf("Test complete.\n");
  return 0;

}
#endif
//*****************************************************************************
//*****************************************************************************
